#include "grammar_scanner.h"
#include <iostream>

GrammarScanner::GrammarScanner()
{
    rules.push_back(Rules());
}
int GrammarScanner::startScanner(const char *file_name)
{
    rules.clear();
    std::ifstream file(file_name);
    int nt_counter = 0;
    states state = X; // початковий стан КА
    char ch;
    while (!file.eof()) {
        if (state == E) return 1; // помилка
        file.get(ch);
        switch (state) {
        case X :
            if (ch == 'G') state = K1;
            else state = E;
            break;
        case K1 :
            if (ch == '(') state = K2;
            else state = E;
            break;
        case K2 :
            if (ch == '{') state = R1;
            else state = E;
            break;
        case R1 :
            if (isupper(ch)) state = E;
            else state = R;
            break;
        case R :
            if (ch == '}') state = F1;
            else if (isupper(ch)) state = E;
            else state = R;
            break;
        case F1 :
            if (ch == ',') state = F;
            else state = E;
            break;
        case F :
            if (ch == '{') state = N1; // блок описання NT-символів
            else state = E;
            break;
        case N1 :
            if (isupper(ch)) state = N;
            else state = E;
            break;
        case N :
            if ((ch == ',') || isupper(ch) || isdigit(ch) || (ch == '_')) state = N;
            else if (ch == '}') state = B1;
            else state = E;
            break;
        case B1 :
            if (ch == ',') state = B;
            else state = E;
            break;
        case B :
            if (ch == 'P') state = A1;
            else state = E;
            break;
        case A1 :
            if (ch == ',') state = A2;
            else state = E;
            break;
        case A2 :
            if (ch == 'S') state = A;
            else state = E;
            break;
        case A :
            if (ch == ')') state = J;
            else state = E;
            break;
        case J :
            if (ch == '\n') state = Q;
            else state = E;
            break;
        case Q :
            if (ch == 'P') state = M;
            else state = E;
            break;
        case M :
            if (ch == ':') state = D;
            else state = E;
            break;
        case D :
            if (ch == '\n') state = Y;
            else state = E;
            break;
        case Y : // зчитуються NT-символи, які містять правила
            if (isupper(ch))
            {
                rules.push_back(Rules(++nt_counter));
                rules.back().setNTSymbol(ch);
                state = Y1;
            }
            else state = E;
            break;
        case Y1 :
            if (isupper(ch) || isdigit(ch) || (ch == '_'))
            {
                rules.back().setNTSymbol(ch);
                state = Y1;
            }
            else if (ch == '-') state = H;
            else state = E;
            break;
        case H :
            if (ch = '>') state = L;
            else state = E;
            break;
        case L : // зчитуються правила
            if ((ch == '\n') || (ch == '|')) state = E; // не може бути порожніх правил
            else
            {
                state = L1;
                rules.back().modifyRule(ch);
            }
            break;
        case L1 :
            if (ch == '|')
            {
                state = L; // переходимо до наступного правила для даного NT-символа
                rules.back().pushRule();
            }
            else if (ch == '\n')
                state = Y; // переходимо до наступного NT-символа в правилах
            else
            {
                state = L1;
                rules.back().modifyRule(ch);
            }
            break;
        }
        ch = 0;
    }
    __removeRepeatNTSymbol();
    return 0; // кінцевий стан КА
}
void GrammarScanner::__removeRepeatNTSymbol()
{
    for (int i = 0; i < rules.size(); i++) {
        for (int j = 0; j < rules[i].size(); j++) {
            std::string tmp;
            for (int z = 0; z < rules[i].getRule(j).size(); z++) {
                if (isupper(rules[i].getRule(j)[z]))
                {
                    tmp += rules[i].getRule(j)[z];
                    continue;
                }
                if (tmp.empty() || (tmp != "REPEATNTSYMBOL"))
                {
                    tmp.clear();
                    continue;
                }
                int s = z - tmp.size() - 1;
                std::string str;
                while (isspace(rules[i].getRule(j)[s])) --s;
                for (; s >= 0; s--) {
                    if (isupper(rules[i].getRule(j)[s]) || isdigit(rules[i].getRule(j)[s]))
                    {
                        str += rules[i].getRule(j)[s];
                        continue;
                    }
                    std::reverse(str.begin(), str.end());
                    Rules &r = findRule(str, rules);
                    r.setRepeatItself(true);
                    rules[i].modifyRule(j, z - tmp.size(), tmp.size()); // видаляємо "символ-повторювач"
                    str.clear();
                    break;
                }
                if (!str.empty())
                {
                    std::reverse(str.begin(), str.end());
                    Rules &r = findRule(str, rules);
                    r.setRepeatItself(true);
                    rules[i].modifyRule(j, z - tmp.size(), tmp.size()); // видаляємо "символ-повторювач"
                }
                tmp.clear();
            }
            if (tmp.empty() || (tmp != "REPEATNTSYMBOL")) continue;
            /* "символ-повторювач" знаходиться в кінці правила */
            int s = rules[i].getRule(j).size() - tmp.size() - 1;
            std::string str;
            while (isspace(rules[i].getRule(j)[s])) --s;
            for (; s > 0; s--) {
                if (isupper(rules[i].getRule(j)[s]) || isdigit(rules[i].getRule(j)[s]))
                {
                    str += rules[i].getRule(j)[s];
                    continue;
                }
                std::reverse(str.begin(), str.end());
                Rules &r = findRule(str, rules);
                r.setRepeatItself(true);
                rules[i].modifyRule(j, rules[i].getRule(j).size() - tmp.size(), tmp.size()); // видаляємо "символ-повторювач"
                break;
            }
        }
    }
}
